import json

from matplotlib import pyplot as plt
import numpy as np
import math
import logging
import traceback


def monte_carlo(per_list=["05", "50", "95"], year=20, count=2000, mean=1.0, std=1.0):
    """蒙特卡洛预测分析"""
    # 随机数种子
    np.random.seed(seed=1000000)

    data = []
    info = []
    temp = []

    per_data = []

    # 按年循环
    for y in range(year):
        rand = np.random.normal(mean, std, count)
        item = mean
        for i in range(count):
            if rand[i] > -1:
                item = rand[i]
            else:
                rand[i] = item

            temp.append(rand[i] + 1)
            pass

        # 第一年
        if y == 0:
            info = temp
        else:
            for j in range(len(temp)):
                if j < len(info):
                    info[j] = info[j] * temp[j]
                    pass
                pass
            pass

        data.append(sorted(info))
        temp = []
        pass

    for y in range(year):
        per_item = []
        for k in range(len(per_list)):
            per_dict = {
                'per_' + per_list[k]: str(float('%.4f' % data[y][int(int(per_list[k]) * count / 100 - 1)]))
            }
            per_item.append(per_dict)
            pass
        per_data.append(per_item)

        pass
    # 预测分析
    forecast = {"year": year, "per_list": per_list, "per_data": per_data}

    # 财富值Wealth Value
    wv_data = {}
    # 年化收益率Annual Return
    ar_data = {}
    for i in [2, 4, 9]:
        # 计算财富值分布
        year_range = i + 1
        wv_data[year_range] = {'data': calculate_distribute(data[i], count)}
        # 计算年化收益率分布
        aror = []
        j = 0
        while j < len(data[i]):
            aror_item = np.power(data[i][j], 1.000 / float(year_range)) - 1.0000
            aror.append(aror_item)
            j += 1
            pass
        ar_data[year_range] = {'data': calculate_distribute(aror, count)}
        pass

    # return json.dumps({"forecast": forecast, "wv_data": wv_data, "ar_data": ar_data})
    return forecast, wv_data, ar_data


def calculate_distribute(ror_list, count):
    """分布计算"""
    try:
        min_ror = min(ror_list)
        max_ror = max(ror_list)
        bound, width, fence = statistics_distribute(min_ror, max_ror, count)

        distribute = []
        i = 1
        while i < fence:

            ror_min = float("%.4f" % (bound + float(i - 1) * width))
            ror_max = float("%.4f" % (bound + i * width))

            ror_num = 0

            j = 0
            while j < len(ror_list):
                if ror_min <= ror_list[j] < ror_max and fence != i:
                    ror_num += 1
                elif ror_min <= ror_list[j] <= ror_max and fence == i:
                    ror_num += 1
                    pass
                j += 1
                pass
            distribute.append({'segment': i, 'ror_min': ror_min, 'ror_max': ror_max, 'ror_num': ror_num})
            i += 1
            pass
        return distribute
    except Exception as e:
        logging.error("error:", traceback.format_exc(e))


def statistics_distribute(min_ror, max_ror, count):
    """分布统计"""
    offset = math.ceil(math.log(count) / math.log(2))
    r = (max_ror - min_ror) / offset
    b = math.floor(math.log(10, r))
    a = math.ceil(r / math.pow(10, b))
    width = a * math.pow(10, b)
    bound = math.floor(min_ror / width) * width
    h = width * offset + bound

    if h < max_ror:
        fence = offset + 1
    else:
        fence = offset
        pass

    return bound, width, fence


if __name__ == '__main__':
    forecast, wv_data, ar_data = monte_carlo(year=10, mean=0.01, std=0.09)

    per_data = forecast.get("per_data")

    per_info = {"per_05": [], "per_50": [], "per_95": []}

    for per_item in per_data:
        for per_dict in per_item:
            key = list(per_dict.keys())[0]
            per_info[key].append(per_dict.get(key))
            pass
        pass

    # 折线图
    per_year = range(forecast.get("year"))
    plt.plot(per_year, per_info["per_05"], marker="+", label="per_05")
    plt.plot(per_year, per_info["per_50"], marker="*", label="per_50")
    plt.plot(per_year, per_info["per_95"], marker="o", label="per_95")
    plt.show()

    # 柱状图

    pass
